/// # FileNode
///
/// 文件树的数据模型。
///
/// ## 支持特性：
/// - **静态 children**：直接传入子节点列表（适合一次性构建的小树）；
/// - **懒加载 children**：提供 [loader]，仅在首次展开时调用，并自动缓存结果；
/// - **文件 / 文件夹**：通过 [isFolder] 区分；
/// - **真实路径**：可选提供 [fullPath]，方便直接执行文件系统操作。
///
/// ## 使用示例
/// - 文件：
/// ```dart
/// FileNode.file(name: 'main.dart', fullPath: '/project/lib/main.dart')
/// ```
///
/// - 静态文件夹：
/// ```dart
/// FileNode.folder(
///   name: 'lib',
///   children: [FileNode.file(name: 'main.dart')],
/// )
/// ```
///
/// - 懒加载文件夹：
/// ```dart
/// FileNode.lazyFolder(
///   name: 'assets',
///   loader: () async => [FileNode.file(name: 'logo.png')],
/// )
/// ```
class FileNode {
  /// 展示名（UI 上显示的名称，不包含路径）
  final String name;

  /// 是否为文件夹
  final bool isFolder;

  /// 真实绝对路径（可选）
  /// - 若提供，则可直接用于文件系统操作；
  /// - 若为空，则需通过 `realPathResolver` 做逻辑路径 → 真实路径的映射。
  final String? fullPath;

  /// 静态子节点（非懒加载）
  /// - 仅当初始化时就已知子节点时使用；
  /// - 与 [loader] 互斥。
  final List<FileNode> children;

  /// 懒加载函数（仅当 [isFolder] = true 时有效）
  /// - 在首次展开文件夹时调用；
  /// - 结果会被缓存到 [FileExplorerController.loadedChildren]。
  final Future<List<FileNode>> Function()? loader;

  /// 常规构造函数
  const FileNode(
      this.name, {
        this.isFolder = false,
        this.fullPath,
        this.children = const [],
        this.loader,
      });

  /// 是否支持懒加载：仅当 **是文件夹且定义了 [loader]** 时为 true
  bool get canLazyLoad => isFolder && loader != null;

  // ────────────────────────────── 工厂构造函数 ──────────────────────────────

  /// 便捷构造：文件节点
  factory FileNode.file({
    required String name,
    String? fullPath,
  }) =>
      FileNode(
        name,
        isFolder: false,
        fullPath: fullPath,
      );

  /// 便捷构造：静态文件夹节点
  factory FileNode.folder({
    required String name,
    String? fullPath,
    List<FileNode> children = const [],
  }) =>
      FileNode(
        name,
        isFolder: true,
        fullPath: fullPath,
        children: children,
      );

  /// 便捷构造：懒加载文件夹节点
  /// - [loader] 在首次展开时调用，并返回子节点列表
  factory FileNode.lazyFolder({
    required String name,
    String? fullPath,
    required Future<List<FileNode>> Function() loader,
  }) =>
      FileNode(
        name,
        isFolder: true,
        fullPath: fullPath,
        loader: loader,
      );
}
